Functions, like all things in ScriptX, are objects (literally,
instances of a subclass of AbstractFunction
,
typically ByteCodeMethod
). There are two ways to
define functions. A named function definition creates a new
variable and immediately assigns the function object to it. An
anonymous function is not assigned to a variable, although it
can be assigned in a subsequent expression. Anonymous
functions are described on page
101.
function fnName [ positionalArgs ] [ #rest restArg ] [ #key keywordArgs ] \The
-> body
function
reserved word, which can be
shortened to simply fn
, signals that this is the
start of a function definition, and fnName is the
name of the variable this function object is assigned to.
After the function name are the parameters to the function:
positionalArgs specify arguments that are required by
the function and must be specified in a specific order
(position), restArg holds an array of optional
arguments given when the function is called, and
keywordArgs is a list of keyword arguments. All three
types of arguments are optional, although they must be
specified in this order. Finally, body is the expression, often a compound expression, that makes up the body of the function. The expressions within the function body are executed when the function is actually called.
local
. Note that
just as with local variables, you can only define a local
function inside a local scope (that is, not at the top
level).local function sortIt theList -> . . .
Additionally, the variable that this function is assigned to
is automatically declared constant
in the
function definition, which means that you cannot assign
anything else to it once the function has been created (except
by redefining another function). This is to prevent
accidentally overwriting the definition of a function with a
simple assignment. If you expect to reuse the global variable
name to which you've assigned a function elsewhere in your
script, consider using the anonymous function construct
instead (or a local variable, if appropriate).
You can specify any number of positional argument parameters (including none), but they always appear first in the function definition, before any "rest" arguments or keyword arguments.
function square n -> n * n
square 10
100
function beepMe -> print "beep!"
beepMe()
"beep!"
If a function has no arguments, empty parentheses are required.
To specify that your function takes keyword arguments, use the
#key
keyword in the function definition, followed
by the definitions of the keywords themselves:
function fnName [ positionalArgs ] [ #rest restArg ] [ #key keywordArgs ] \Keyword arguments are optional in the function definition, but if they are included, they must be specified after both the positional arguments and the optional "rest" argument (described in the next section).
-> body
You can specify any number of keyword arguments after the
#key
reserved word. Keyword arguments look like
this:
keyName:[ argName ] [ ( initialValue ) ] . . .where:
unsupplied
object.
Note that unsupplied
is different from
undefined
; undefined
must be
specifically indicated by the user for it to appear in the
body of a definition (and may have a specific meaning), but
unsupplied
is used if the user did not specify
that key at all.
-- reportArgs simply prints out its a and b values
function reportArgs #key a: b: -> (print a; print b)
reportArgs a:10 b:20
10
20
reportArgs a:100
100
unsupplied
reportArgs a:10 b:30 a:50
10
30
-- reportArgs2 is the same as reportArgs, but the values of the
-- keys are assigned to the local variables moo and quack -- instead of a and b.
function reportArgs2 #key a:moo b:quack -> (
print moo; print quack
)
reportArgs2 a:10
10
unsupplied
-- reportArgs3 defines default values for a and b so -- neither will appear as unsupplied
function reportArgs3 #key a:(1) b:(2) -> (
print a; print b
)
reportArgs3() -- use all defaults
1
2
reportArgs3 a:20
20
2
-- reportArgs4 defines default values -- and assigns them to local variables
function reportArgs4 #key a:moo(1) b:quack(2) -> (
print moo; print quack
)
reportArgs4()
1
2
reportArgs4 a:6
6
2
The #rest
argument allows you to define a
function with a variable number of arguments. In this sense,
"rest" means "the rest of the arguments after the positional
arguments, except the denoted keyword arguments." The function
collects those "rest" values into an array, and in the body of
the function you can access elements in that array.
Unlike positional arguments, rest arguments are not required-when you call the function you can supply no rest arguments, or as many as you need to.
Notice in the following function definition that
#rest
takes a single argument restArg.
The combination #rest
restArg comes
after any positional arguments and before any keyword
arguments. By convention, the argument args
is
typically used for restArg (though you could name it
whatever you want), so what you often see in examples is the
combination #rest
args
.
function fnName [ positionalArgs ] [ #rest restArg ] [ #key keywordArgs ] \This is an example of a function that takes no arguments other than its rest arguments:
-> body
-- addEmUp takes a variable number of arguments, sums them all -- and returns the sum
function addEmUp #rest args -> (
local sumArgs := 0
for i in args do sumArgs := sumArgs + i
)
addEmUp 1 2 3 4 5
15
addEmUp 3 4
7
In place of using args
in the previous example,
we could have used numList
, to denote the list of
numbers.
This example defines a function that takes a single required argument and a variable number of rest arguments (each argument happens to itself be an array):
-- joinArray takes a single required array and a variable number -- of other arrays. It builds a single array of all the -- elements in all its arguments
function joinArray array1 #rest otherArrays -> (
for i in otherArrays do addMany array1 i
return array1
)
joinArray #(1,2,3) #(4,5,6)
#(1, 2, 3, 4, 5, 6)
joinArray #() #(@angora,@persian) #(@egyptianmau) #(@housecat)
#(@angora, @persian, @egyptianmau, @housecat)
If your function definition uses both the #rest
and #key
reserved words to define both rest and
keyword arguments, the non-positional arguments can only be in
the form of keyword-value pairs
(keyword:value). You cannot mix arbitrary
numbers of #rest
arguments and keyword
arguments.
When a function with both rest and keyword arguments is
called, the keywords and values supplied to the function are
stored into the rest argument in sequence (key, value, key,
value, and so on) with the keywords appearing as
NameClass
objects.
function showRest #rest args #key a: b: c:(100) ->
print args debug
showRest a:10 b:20 c:30
#(@a, 10, @b, 20, @c, 30)
showRest a:10
#(@a, 10)
showRest()
#()
Note that the compiler translates a keyword argument into a pair of conventional arguments. The following function calls are equivalent:
grok foo:10 moof:20
grok @foo 10 @moof 20
return
block control expression:return expressionWhen a
return
expression is encountered in the
body of a function definition, the function is immediately
exited with the value specified by expression. Functions that do not specify a specific return value return the value of the last expression evaluated while executing the function.
function factorial n -> (
if n <= 0 then return 1
else return (n * (factorial (n - 1)))
)
-- now, try a few test values
factorial 0
1
factorial 4
24
function sumAndPrint #rest args -> (
local mySum := 0
for i in args do mySum := mySum + i
format debug "The sum is: %*\n" mySum @normal
return mySum
)
sumAndPrint 10 20 34 45
The sum is: 109
109
This document is part of the ScriptX Language Guide, one of the volumes of the ScriptX Technical Reference Series. ScriptX is developed by the ScriptX Engineering Team at Apple Computer, successor to the Kaleida Engineering Team at Kaleida Labs, Inc.